﻿using System;
using System.Globalization;

namespace WGS84
{
    public class Bearing
    {
        readonly Degrees _bearingInDegrees;
        readonly Radians _bearingInRadians;

        /// <summary>
        /// Instance constructor
        /// </summary>
        /// <param name="bearing">Bearing in radians</param>
        public Bearing(Radians bearing)
        {
            _bearingInRadians = bearing;
            _bearingInDegrees = new Degrees(bearing.ToDegrees());
        }

        /// <summary>
        /// Instance constructor
        /// </summary>
        /// <param name="bearing">Bearing in degrees</param>
        public Bearing(Degrees bearing)
        {
            _bearingInDegrees = bearing;
            _bearingInRadians = new Radians(bearing.ToRadians());
        }

        /// <summary>
        /// Instance constructor
        /// </summary>
        /// <param name="ordinal">Ordinal value of bearing, e.g. N = 0 degrees, E = 90 degrees</param>
        public Bearing(char ordinal)
        {
            if (ordinal == 'U')
            {
                return;
            }
            switch (ordinal)
            {
                case 'N':
                    _bearingInDegrees = new Degrees(0);
                    break;
                case 'E':
                    _bearingInDegrees = new Degrees(90);
                    break;
                case 'S':
                    _bearingInDegrees = new Degrees(180);
                    break;
                case 'W':
                    _bearingInDegrees = new Degrees(270);
                    break;
                default:
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Ordinal {0} is invalid. Must be a compass direction", ordinal));

            }
            _bearingInRadians = new Radians(_bearingInDegrees.ToRadians());
        }

        /// <summary>
        /// Bearing in radians
        /// </summary>
        /// <returns>Radians value of bearing</returns>
        public Radians ToRadians()
        {
            return _bearingInRadians;
        }
        /// <summary>
        /// Bearing in degrees
        /// </summary>
        /// <returns>Value of bearing in degrees</returns>
        public Degrees ToDegrees()
        {
            return _bearingInDegrees;
        }
        /// <summary>
        /// Converts bearing to Compass ordinal value
        /// </summary>
        /// <returns>N, E, S, W depending on bearing</returns>
        public char ToOrdinal()
        {
            if (_bearingInDegrees == null)
            {
                return 'U';
            }
            double bearing = _bearingInDegrees.ToDouble();
            bearing = bearing % 360.0;
            if (bearing < 45)
                return 'N';

            if (bearing < 135)
                return 'E';

            if (bearing < 225)
                return 'S';

            if (bearing < 315)
                return 'W';

            return 'N';
        }

        /// <summary>
        /// Converts bearing to a string value, e.g. North, South etc
        /// </summary>
        /// <returns>North, South, East, West depending on bearing</returns>
        public string ToDirection()
        {
            if (_bearingInDegrees == null)
            {
                return "Unknown";
            }
            double bearing = _bearingInDegrees.ToDouble();
            bearing = bearing % 360;
            if (bearing < 45)
                return "North";

            if (bearing < 135)
                return "East";

            if (bearing < 225)
                return "South";

            if (bearing < 315)
                return "West";

            return "North";
        }

        /// <summary>
        /// Calculate Hash code for this object
        /// </summary>
        /// <returns>Hashcode based on member variables</returns>
        public override int GetHashCode()
        {
            int hash = 0;
            hash ^= _bearingInRadians.GetHashCode();
            hash ^= _bearingInDegrees.GetHashCode();
            return hash;
        }

        /// <summary>
        /// Compares by value two Bearing objects
        /// </summary>
        /// <param name="obj">object for comparison</param>
        /// <returns>Whether they are equal</returns>
        public override bool Equals(object obj)
        {
            if (obj == null) return false;

            if (ReferenceEquals(this, obj)) 
                return true;

            if (GetType() != obj.GetType()) 
                return false;

            Bearing that = (Bearing)obj;

            if (!Equals(_bearingInDegrees, that._bearingInDegrees))
                return false;

            if (!Equals(_bearingInRadians, that._bearingInRadians))
                return false;

            return true;
        }

        /// <summary>
        /// Get legible string
        /// </summary>
        /// <returns>Human readable string</returns>
        public override string ToString()
        {
            return string.Format(CultureInfo.CurrentCulture, "{0} Degrees {1} Radians", _bearingInDegrees, _bearingInRadians);
        }
    }
}
